package com.gitee.fanx.retty;

import com.gitee.fanx.retty.web.BaseExceptionHandler;
import com.gitee.fanx.retty.web.ExceptionHandler;
import com.gitee.fanx.retty.web.ResponseHandler;
import com.gitee.fanx.retty.web.RouteMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * Netty-Http服务器
 *
 * @author fanhang
 */
public final class NettyHttpServer {

	private final static Logger logger = LoggerFactory.getLogger(NettyHttpServer.class);

	/**
	 * 监听端口号
	 */
	private int port = 1234;

	private NettyHttpOption nettyHttpOption;
	private RouteMapping routeMapping;
	private ResponseHandler responseHandler;
	private ExceptionHandler exceptionHandler;

	public NettyHttpServer(int port, NettyHttpOption nettyHttpOption, RouteMapping routeMapping) {
		this.port = port;
		this.nettyHttpOption = nettyHttpOption;
		this.routeMapping = routeMapping;
	}

	public NettyHttpServer withResponseHandler(ResponseHandler responseHandler) {
		this.responseHandler = responseHandler;
		return this;
	}

	public NettyHttpServer withRouteExceptionHandler(ExceptionHandler exceptionHandler) {
		this.exceptionHandler = exceptionHandler;
		return this;
	}

	/**
	 * 启动服务
	 * @throws InterruptedException
	 */
	public void start() throws InterruptedException {
		if (this.exceptionHandler == null) {
			this.exceptionHandler = new BaseExceptionHandler();
		}

		// BossGroup处理nio的Accept事件（TCP连接）
		NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
		// Worker处理nio的Read和Write事件（通道的I/O事件）
		NioEventLoopGroup workerGroup = new NioEventLoopGroup(2);
		try {
			// handler在初始化时就会执行，而childHandler会在客户端成功connect后才执行。
			ServerBootstrap bootstrap = new ServerBootstrap();

			NettyHandlerInitializer handlerInitializer = new NettyHandlerInitializer(this.nettyHttpOption.getMaxContentLength(), routeMapping, responseHandler, exceptionHandler);

			bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
					.option(ChannelOption.SO_BACKLOG, 128)
					.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
					.childHandler(handlerInitializer);

			ChannelFuture f = bootstrap.bind(port).sync();
			logger.info("The retty is now ready to accept http-requests on port {}", this.port);
			f.channel().closeFuture().sync();
		}
		finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}

}
